#include <QtConcurrent>
#include <QFile>
#include <QFileInfo>
#include <QHostAddress>
#include <QVector>
#include <QThread>
#include <QDebug>

#include <QRegularExpression>

#include "transfersocket.h"
#include "tcpblock.h"
#include "tcpmanager.h"
#include "FileBlock.h"

static const int maxPayLoadSize = 10240;

TransferSocket::TransferSocket(QObject *parent, TcpManager *manager) :
    QTcpSocket(parent),
    m_maxTaskNum(0),
    m_barInterval(1000),
    m_manager(manager)
{
    connect(this, &QTcpSocket::readyRead,
            this, &TransferSocket::onMessage, Qt::DirectConnection);
    qDebug() << "New Socket run in thread" << thread()->currentThreadId();
}

TransferSocket::~TransferSocket()
{
    qDebug() << "Tcp socket " << socketName() << " destory";
}

void TransferSocket::setSocketName(const QString& name)
{
    if (!runInLoop()) {
        QMetaObject::invokeMethod(this, "setSocketName",
                                  Q_ARG(QString, std::move(name)));
        return;
    }
    setObjectName(name);
}

QString TransferSocket::socketName() const
{
    return objectName();
}

void TransferSocket::setBarInterval(const qint64 miscellaneous)
{
    if (!runInLoop())
    {
        QMetaObject::invokeMethod(this, "setBarInterval",
                                  Q_ARG(qint64, miscellaneous));
        return;
    }
    m_barInterval = miscellaneous;
}

void TransferSocket::sendFigure(QByteArray data)
{
    /* 一个数据块大小 */
    if (!runInLoop()) {
        QMetaObject::invokeMethod(this, "sendFigure", Q_ARG(QByteArray, data));
        return;
    }
    assert(state() == SocketState::ConnectedState);
    TcpBlock block;
    QByteArray message;
    QDataStream out(&message, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_5_12);
    block.dataBlock = std::move(data);          // 填充数据
    block.blockSize = block.tcpBlockSize();     // 设置数据块大小
    block.magicNumber = TcpManager::magicNumber();
    block.type = TransferType::CANVAS_TYPE;
    out << block;   /* out操作会将blockSize转为网络序 */
    writeToSocket(std::move(message));
}

void TransferSocket::disconnectFromHost()
{
    QTcpSocket::disconnectFromHost();
}

void TransferSocket::setNewTcpBlockCallBack(const NewTcpBlockCallBack &callback)
{
    m_newTcpBlockCallBack = callback;
}

void TransferSocket::sendFile(QSharedPointer<QFile> file)
{
    if (!runInLoop()) {
        QMetaObject::invokeMethod(this, "sendFile", Q_ARG(QSharedPointer<QFile>, file));
        return;
    }
    assert(state() == SocketState::ConnectedState);

    qint64 barInterval = m_barInterval;
    qint64 magicNumber = TcpManager::magicNumber();


    // 先发文件元数据
    TcpBlock headBlock;
    headBlock.type        = TransferType::FILE_HEADER;
    headBlock.magicNumber = magicNumber;
    FileHeader header;

    static QRegularExpression regex("[^/]*\\.([a-z]|[A-Z]|[0-9])+$");
    QRegularExpressionMatch match = regex.match(file->fileName());
    qDebug() << "Send file name" << match.captured() << "size" << file->size();
    qDebug() << "Start send file" << file->fileName();
    header.fileName   = match.captured();
    header.fileSize   = file->size();
    headBlock.dataBlock  = header.toByteArray();
    headBlock.blockSize  = headBlock.tcpBlockSize();
    QByteArray headMessage;
    QDataStream headMessageStream(&headMessage, QIODevice::WriteOnly);
    headMessageStream << headBlock;
    qDebug() << headBlock;
    writeToSocket(std::move(headMessage));
    qDebug() << "Send headBlock success.";

    // 开始传输文件
    qint64 offset = 0;
    TcpBlock block;
    block.type        = TransferType::FILE_BODY;
    block.magicNumber = magicNumber;
    QElapsedTimer timer;
    timer.start();

    while (!file->atEnd())
    {
        file->startTransaction();
        block.dataBlock = file->read(maxPayLoadSize);
        file->commitTransaction();
        block.blockSize = block.tcpBlockSize();     // 整个块的大小
        offset += block.dataBlock.size();

        QByteArray message;
        QDataStream out(&message, QIODevice::WriteOnly);
        out.setVersion(QDataStream::Qt_5_12);
        // 将主机序转为了网络序
        out << block;
        writeToSocket(std::move(message));

        if (timer.elapsed() >= barInterval || file->atEnd()) {
            timer.restart();
            emit writingFileBar(file, block.magicNumber, offset);
        }
    }
    // NOTE 发送传输完成的信号
    emit sendFileComplete(file);
    timer.invalidate();
}

void TransferSocket::onMessage()
{
    static QElapsedTimer timer;
    timer.start();
    while (!this->atEnd())
    {
        QSharedPointer<TcpBlock> block(new TcpBlock);
        if (this->bytesAvailable() < (qint64)sizeof(block->blockSize)) {
            break;
        }

        QByteArray size = this->peek(sizeof(block->blockSize));
        /* 读取到的blockSize为网络序 */
        QDataStream inSize(&size, QIODevice::ReadOnly);
        inSize >> block->blockSize;

        if (this->bytesAvailable() < block->blockSize) {
            // FIX: 可能存在潜在的问题
            break;
        }

//        qDebug() << "Tcp socket" << socketName() << "read count:" << block->blockSize;

        QByteArray data = this->read(block->blockSize);
        QDataStream in(&data, QIODevice::ReadWrite);
        in >> *block.get();
        m_newTcpBlockCallBack(block);
    }
}

bool TransferSocket::runInLoop() const
{
    return thread() == QThread::currentThread();
}

void TransferSocket::writeToSocket(QByteArray data)
{
    QTcpSocket::write(data);
    waitForBytesWritten(3000);
}
